﻿<!DOCTYPE HTML>
<html lang="zh">
<head>
<title>DllCall - 语法 &amp; 使用 | AutoHotkey v2</title>
<meta name="description" content="The DllCall function calls a function inside a DLL, such as a standard Windows API function." />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link href="../static/theme.css" rel="stylesheet" type="text/css" />
<script src="../static/content.js" type="text/javascript"></script>
<script type="text/javascript">$(function(){0<=window.navigator.userAgent.toLowerCase().indexOf("ucbrowser")&&CaoNiMaDeUc()})</script>
</head>
<body>

<h1>DllCall</h1>

<p>调用 DLL 文件中的函数, 例如标准的 Windows API 函数.</p>

<pre class="Syntax">Result := <span class="func">DllCall</span>("<span class="optional">DllFile\</span>Function" <span class="optional">, Type1, Arg1, Type2, Arg2, "Cdecl ReturnType"</span>)</pre>
<h2 id="Parameters">参数</h2>
<dl>

  <dt>[DllFile\]Function</dt>
  <dd>
      <p>类型: <a href="../Concepts.htm#strings">字符串</a>或<a href="../Concepts.htm#numbers">整数</a></p>
      <p>DLL 或 EXE 文件名后面跟着一个反斜杠和函数名. 例如: <code>&quot;MyDLL\MyFunction&quot;</code>(文件扩展名省略时默认为 &quot;.dll&quot;). 如果未指定绝对路径, 则假定 <em>DllFile</em> 在系统的 PATH 指定的路径或 <a href="../Variables.htm#WorkingDir">A_WorkingDir</a> 中.</p>
      <p id="std">当调用位于 User32.dll, Kernel32.dll, ComCtl32.dll 或 Gdi32.dll 中的函数时, 可以省略 <em>DllFile</em>. 例如, <code>&quot;User32\IsWindowVisible&quot;</code> 和 <code>&quot;IsWindowVisible&quot;</code> 产生相同的结果.</p>
      <p>如果使用指定的名称无法找到函数, 则自动附加 "W"(Unicode) 前缀. 例如, <code>"MessageBox"</code> 等同于 <code>"MessageBoxW"</code>.</p>
      <p>当 <em>重复 </em> 调用 DLL 时, 通过<a href="#load">预先加载此 DLL 文件</a>可以显著改善执行效率.</p>
      <p>此参数也可以仅是一个表示需调用函数的内存地址的整数. 如 <a href="#COM">COM</a> 和 <a href="CallbackCreate.htm">CallbackCreate</a> 提供的地址.</p>
  </dd>

  <dt>Type1, Arg1</dt>
  <dd>
      <p>类型: <a href="../Concepts.htm#strings">字符串</a></p>
      <p>这样的每对数据表示需传递给函数的单个参数. 参数的个数没有限制. 关于 <em>Type</em>, 请参阅请参阅下面的<a href="#types">类型表</a>. 关于 <em>Arg</em>, 指定传递给函数的值.</p>
  </dd>

  <dt>Cdecl ReturnType</dt>
  <dd>
      <p>类型: <a href="../Concepts.htm#strings">字符串</a></p>
      <p id="cdecl">通常省略单词 <em>Cdecl</em>, 因为大多数函数使用标准调用约定而不是 &quot;C&quot; 调用约定(像 wsprintf 这样接受可变数目参数的函数在这点上是个例外). 注意, 不支持大多数面向对象的 C++ 函数使用的 <i>thiscall</i> 约定.</p>
      <p>如果此参数存在, 那么单词 <em>Cdecl</em> 应该在返回值类型前列出(如果有). 在单词间使用空格或 tab 分隔. 例如: <code>&quot;Cdecl Str&quot;</code>.</p>
      <p>因为在 64 位代码中不存在单独的 "C" 调用约定, 所以在 AutoHotkey 的 64 位版本中可以使用 <i>Cdecl</i> 但没有效果.</p>
      <p><em>ReturnType</em>: 如果函数返回 32 位的有符号整型(Int), BOOL, 或没有返回值, 则 <em>ReturnType</em> 可以省略. 否则, 需要指定下面<a href="#types">类型表</a>中参数类型的其中一个. 还支持<a href="#asterisk">星号后缀</a>.</p>
  </dd>

</dl>

<h2 id="Return_Value">返回值</h2>
<p>类型: <a href="../Concepts.htm#strings">字符串</a>或<a href="../Concepts.htm#numbers">整数</a></p>
<p>DllCall 返回 <em>Function</em> 返回的实际值. 如果 <em>Function</em> 的类型不返回值, the result is an undefined value of the specified return type (默认为整数).</p>

<h2 id="types">参数和返回值的类型</h2>
<table class="info">
<tr id="str">
<td>Str</td>
<td><p>例如 &quot;Blue&quot; 或 MyVar 这样的字符串. 如果被调用函数修改了字符串且此参数是个裸变量, 那么其内容将被更新. 例如, 后面的调用将把 <em>MyVar</em> 的内容转换成大写: <code>DllCall(&quot;CharUpper&quot;, &quot;Str&quot;, <i>MyVar</i>)</code>.</p>
  <p>If the function is designed to store a string longer than the parameter's input value (or if the parameter is for output only), the recommended approach is to use <a href="BufferAlloc.htm">BufferAlloc</a> to allocate a buffer, the <a href="#ptr">Ptr</a> type to pass it, and <a href="StrGet.htm">StrGet</a> to retrieve the string after the function returns. </p>
  <p>Otherwise, ensure that the variable is large enough before calling the function. This can be achieved by calling <code><a href="VarSetCapacity.htm">VarSetCapacity</a>(MyVar, 123)</code>, where 123 is the number of <strong>bytes</strong> that <em>MyVar</em> must be able to hold. If the variable is not null-terminated upon return, an error message is shown and the program exits as it is likely that memory has been corrupted via buffer overrun. This would typically indicate that the variable's capacity was insufficient.</p>
  <p></p>
  <p><em>Str</em> 参数不能是计算结果为数字(例如 <code>i+1</code>) 这样的<a href="../Variables.htm#Expressions">表达式</a>. 如果如此, 则不会调用函数并抛出<a href="Catch.htm#RuntimeErrors">异常</a>.</p>
  <p id="strp">The rarely-used <a href="#asterisk">Str*</a> arg type passes the address of a temporary variable containing the address of the string. If the function writes a new address into the temporary variable, the new string is copied into the script's variable, if one was passed. This can be used with functions that expect something like &quot;TCHAR **&quot; or &quot;LPTSTR *&quot;. However, if the function allocates memory and expects the caller to free it (such as by calling <a href="https://docs.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-cotaskmemfree">CoTaskMemFree</a>), the <code>Ptr*</code> arg type must be used instead.</p>
  <p class="note"><strong>Note</strong>: When passing a string to a function, be aware what <a href="../Compat.htm#DllCall"><i>type</i> of string</a> the function expects.</p></td>
</tr>
<tr>
  <td><span id="wstr"></span>WStr</td>
  <td>Since AutoHotkey uses UTF-16 natively, WStr (wide character string) is equivalent to Str.</td>
</tr>
<tr>
  <td><span id="astr"></span>AStr</td>
  <td>
    <p>AStr causes the input value to be automatically converted to ANSI. Since the temporary memory used for this conversion is only large enough for the converted input string, any value written to it by the function is discarded. To receive an ANSI string as an output parameter, follow this example:</p>
<pre>buf := BufferAlloc(<i>length</i>)  <em>; Allocate temporary buffer.</em>
DllCall("<i>Function</i>", "ptr", buf)  <em>; Pass buffer to function.</em>
str := StrGet(buf, "cp0")  <em>; Retrieve ANSI string from buffer.</em>
</pre>
    <p>The rarely-used <a href="#asterisk">AStr*</a> arg type is also supported and behaves similarly to the <a href="#strp">Str*</a> type, except that any new string is converted from ANSI to the native format, UTF-16.</p>
    <p>See <a href="../Compat.htm#DllCall">Script Compatibility</a> for equivalent Win32 types and other details.</p>
  </td>
</tr>
<tr>
  <td>Int64</td>
  <td>A 64-bit integer, whose range is -9223372036854775808 (-0x8000000000000000) to 9223372036854775807 (0x7FFFFFFFFFFFFFFF).</td>
</tr>
<tr id="Int">
  <td>Int</td>
  <td><p>A 32-bit integer (the most common integer type), whose range is -2147483648 (-0x80000000) to 2147483647 (0x7FFFFFFF). An Int is sometimes called a &quot;Long&quot;.</p>
    <p>An Int should also be used for each BOOL argument expected by a function (a BOOL value should be either 1 or 0).</p>
    <p>An <a href="#unsigned">unsigned</a> Int (UInt) is also used quite frequently, such as for DWORD.</p></td>
</tr>
<tr>
  <td>Short</td>
  <td>A 16-bit integer, whose range is -32768 (-0x8000) to 32767 (0x7FFF). An <a href="#unsigned">unsigned</a> Short (UShort) can be used with functions that expect a WORD.</td>
</tr>
<tr>
  <td>Char</td>
  <td>An 8-bit integer, whose range is -128 (-0x80) to 127 (0x7F). An <a href="#unsigned">unsigned</a> character (UChar) can be used with functions that expect a BYTE.</td>
</tr>
<tr>
  <td>Float</td>
  <td>A 32-bit floating point number, which provides 6 digits of precision.</td>
</tr>
<tr>
  <td>Double</td>
  <td>A 64-bit floating point number, which provides 15 digits of precision.</td>
</tr>
<tr id="ptr">
  <td>Ptr</td>
  <td><p>A <a href="../Variables.htm#PtrSize">pointer-sized</a> integer, equivalent to Int or Int64 depending on whether the exe running the script is 32-bit or 64-bit. <i>Ptr</i> should be used for pointers to arrays or structures (such as RECT* or LPPOINT) and almost all handles (such as HWND, HBRUSH or HBITMAP). If the parameter is a pointer to a single numeric value such as LPDWORD or int*, generally the * or P suffix should be used instead of &quot;Ptr&quot;.</p>
  <p>If an object is passed to a Ptr parameter, the value of the object's <code>Ptr</code> property is used. If no such property exists, an exception is thrown. Typically the object would be a <a href="../objects/Buffer.htm">Buffer</a> object created by <a href="BufferAlloc.htm">BufferAlloc</a>.</p>
  <p>If an object is passed to a Ptr* parameter, the value of the object's <code>Ptr</code> property is retrieved before the call and the address of a temporary variable containing this value is passed to the function. After the function returns, the new value is assigned back to the object's <code>Ptr</code> property.</p>
  <p><i>Ptr</i> can also be used with the * or P suffix; it should be used with functions that output a pointer via LPVOID* or similar.</p>
  <p><i>UPtr</i> is also valid, but is only unsigned in 32-bit builds as AutoHotkey does not support unsigned 64-bit integers.</p>
  <p class="note"><strong>Note</strong>: To pass a <strong>NULL</strong> handle or pointer, pass the integer 0.</p></td>
</tr>
<tr id="asterisk">
  <td>* or P<br>
    (suffix)</td>
  <td><p>Append an asterisk (with optional preceding space) to any of the above types to cause the address of the argument to be passed rather than the value itself (the called function must be designed to accept it). Since the value of such an argument might be modified by the function, whenever a naked variable is passed as the argument, that variable's contents will be updated. 例如, the following call would pass the contents of MyVar to MyFunction by address, but would also update MyVar to reflect any changes made to it by MyFunction: <code>DllCall(&quot;MyDll\MyFunction&quot;, &quot;Int*&quot;, MyVar)</code>.</p>
    <p>In general, an asterisk is used whenever a function has an argument type or return type that starts with &quot;LP&quot;. The most common example is LPDWORD, which is a pointer to a DWORD. Since a DWORD is an unsigned 32-bit integer, use &quot;UInt*&quot; or &quot;UIntP&quot; to represent LPDWORD. An asterisk should not be used for string types such as LPTSTR, pointers to structures such as LPRECT, or arrays; for these, <a href="#str">&quot;Str&quot;</a> or &quot;Ptr&quot; should be used, depending on whether you pass a variable or its address.</p>
    <p class="note"><strong>Note</strong>: &quot;Char*&quot; is not the same as <a href="#str">&quot;Str&quot;</a> because &quot;Char*&quot; passes the address of an 8-bit number, whereas <a href="#str">&quot;Str&quot;</a> passes the address of a series of characters, which may be either 16-bit (Unicode) or 8-bit (for "AStr"), depending on the version of AutoHotkey. Similarly, &quot;UInt*&quot; passes the address of a 32-bit number, so should not be used if the function expects an array of values or a structure larger than 32 bits.</p>
    <p>Since variables in AutoHotkey have no fixed type, the address passed to the function points to temporary memory rather than the variable itself. It is not necessary to call <a href="VarSetCapacity.htm">VarSetCapacity</a> on the variable as DllCall will update it correctly after the function returns.</p>
    </td>
</tr>
<tr id="unsigned">
  <td>U (prefix)</td>
  <td><p>Prepend the letter U to any of the integer types above to interpret it as an unsigned integer (UInt64, UInt, UShort, and UChar). Strictly speaking, this is necessary only for return values and <a href="#asterisk">asterisk variables</a> because it does not matter whether an argument passed by value is unsigned or signed (except for Int64).</p>
    <p>If a negative integer is specified for an unsigned argument, the integer wraps around into the unsigned domain. 例如, when -1 is sent as a UInt, it would become 0xFFFFFFFF.</p>
    <p><em>Unsigned</em> 64-bit integers produced by a function are not supported. Therefore, to work with numbers greater or equal to 0x8000000000000000, omit the U prefix and interpret any negative values received from the function as large integers. 例如, a function that yields -1 as an Int64 is really yielding 0xFFFFFFFFFFFFFFFF if it is designed to yield a UInt64.</p>
    </td>
</tr>
<tr id="HRESULT">
  <td>HRESULT</td>
  <td>
    <p>A 32-bit integer. This is generally used with COM functions and is valid only as a return type without any prefix or suffix. Error values (as defined by the <a href="https://docs.microsoft.com/en-us/windows/win32/api/winerror/nf-winerror-failed">FAILED macro</a>) are never returned; instead, an exception is thrown, with <code>Extra</code> set to the hexadecimal error code. Therefore, the return value is a success code in the range 0 to 2147483647.</p>
    <p>HRESULT is the default return type for <a href="ComCall.htm">ComCall</a>.</p>
  </td>
</tr>
</table>

<h2 id="error">Errors</h2>
<p>DllCall throws an <a href="Catch.htm#RuntimeErrors">exception</a> under any of the following conditions:</p>
<ul>
  <li>The <a href="#HRESULT">HRESULT</a> return type was used and the function returned an error value (as defined by the <a href="https://docs.microsoft.com/en-us/windows/win32/api/winerror/nf-winerror-failed">FAILED macro</a>). <code>Exception.Extra</code> contains the hexadecimal error code.</li>
  <li>The <em>[DllFile\]Function</em> parameter is a floating point number. A string or positive integer is required.</li>
  <li>The <a href="#types">return type</a> or one of the specified <a href="#types">arg types</a> is invalid.</li>
  <li>An argument was passed a value of an unexpected type. For instance, an <a href="../Variables.htm#Expressions">expression</a> that evaluates to a number was passed to a string (<a href="#str">str</a>) argument, a non-numeric string was passed to a numeric argument, or an object was passed to an argument <em>not</em> of type <a href="#ptr">Ptr</a>.</li>
  <li>The specified <em>DllFile</em> could not be accessed or loaded. If no explicit path was specified for <em>DllFile</em>, the file must exist in the system's PATH or <a href="../Variables.htm#WorkingDir">A_WorkingDir</a>. This error might also occur if the user lacks permission to access the file, or if AutoHotkey is 32-bit and the DLL is 64-bit or vice versa.</li>
  <li>The specified function could not be found inside the DLL.</li>
  <li>The function was called but it aborted with a fatal exception. <code>Exception.Extra</code> contains the exception code. 例如, 0xC0000005 means &quot;access violation&quot;. In such cases, the thread is aborted (if <a href="Try.htm">try</a> is not used), but any <a href="#asterisk">asterisk variables</a> are still updated. An example of a fatal exception is dereferencing an invalid pointer such as NULL (0). Since a <a href="#cdecl">Cdecl</a> function never produces the error described in the next paragraph, it may generate an exception when too few arguments are passed to it.</li>
  <li>The function was called but was passed too many or too few arguments. <code>Exception.Extra</code> contains the number of bytes by which the argument list was incorrect. If it is positive, too many arguments (or arguments that were too large) were passed, or the call requires <a href="#cdecl">CDecl</a>. If it is negative, too few arguments were passed. This situation should be corrected to ensure reliable operation of the function. The presence of this error may also indicate that an exception occurred. Note that due to the x64 calling convention, 64-bit builds never raise this error.</li>
</ul>

<h2 id="except">Native Exceptions and A_LastError</h2>
<p>In spite of the built-in exception handling, it is still possible to crash a script with DllCall. This can happen when a function does not directly generate an exception but yields something inappropriate, such as a bad pointer or a string that is not terminated. This might not be the function's fault if the script passed it an unsuitable value such as a bad pointer or a <a href="#str">&quot;str&quot;</a> with insufficient capacity. A script can also crash when it specifies an inappropriate argument type or return type, such as claiming that an ordinary integer yielded by a function is an <a href="#asterisk">asterisk variable</a> or <a href="#str">str</a>.</p>
<p>The built-in variable <a href="../Variables.htm#LastError">A_LastError</a> contains the result of the operating system's GetLastError() function.</p>

<h2 id="load">Performance</h2>
<p>When making repeated calls to a DLL, performance can be dramatically improved by loading it explicitly (<em>this is not necessary for a <a href="#std">standard DLL</a> such as User32 because it is always resident</em>). This practice avoids the need for DllCall to internally call LoadLibrary and FreeLibrary each time. 例如:</p>
<pre>hModule := DllCall(&quot;<strong>LoadLibrary</strong>&quot;, &quot;Str&quot;, &quot;MyFunctions.dll&quot;, &quot;Ptr&quot;)  <em>; Avoids the need for DllCall in the loop to load the library.</em>
Loop Files, "C:\My Documents\*.*", "R"
    result := DllCall(&quot;MyFunctions\BackupFile&quot;, &quot;Str&quot;, A_LoopFilePath)
DllCall(&quot;<strong>FreeLibrary</strong>&quot;, &quot;Ptr&quot;, hModule)  <em>; To conserve memory, the DLL may be unloaded after using it.</em></pre>
<p>Even faster performance can be achieved by looking up the function's address beforehand. 例如:</p>
<pre><em>; In the following example, if the DLL isn't yet loaded, use LoadLibrary in place of GetModuleHandle.</em>
<strong>MulDivProc</strong> := DllCall(&quot;GetProcAddress&quot;, &quot;Ptr&quot;, DllCall(&quot;GetModuleHandle&quot;, &quot;Str&quot;, &quot;<strong>kernel32</strong>&quot;, &quot;Ptr&quot;), &quot;AStr&quot;, &quot;<strong>MulDiv</strong>&quot;, &quot;Ptr&quot;)
Loop 500
    DllCall(<strong>MulDivProc</strong>, &quot;Int&quot;, 3, &quot;Int&quot;, 4, &quot;Int&quot;, 3)</pre>
<p>If DllCall's first parameter is a literal string such as <code>"MulDiv"</code> and the DLL containing the function is ordinarily loaded before the script starts, or has been successfully loaded with <a href="_DllLoad.htm">#DllLoad</a>, the string is automatically resolved to a function address. This built-in optimization is more effective than the example shown above.</p>
<p>Finally, when passing a string-variable to a function that will not change the length of the string, performance is improved by passing the variable <a href="../Variables.htm#amp">by address</a> (e.g. &amp;MyVar) rather than as a &quot;<a href="#str">str</a>&quot; (especially when the string is very long). The following example converts a string to uppercase: <code>DllCall(&quot;CharUpper&quot;, &quot;<strong>Ptr</strong>&quot;, <strong>&amp;</strong>MyVar, &quot;Ptr&quot;)</code>.</p>

<h2 id="struct">Structures and Arrays</h2>
<p>A structure is a collection of <em>members</em> (fields) stored adjacently in memory. Most members tend to be integers.</p>
<p>Functions that accept the address of a structure (or a memory-block array) can be called by storing the structure's raw binary data in a normal variable. The following steps are generally used:</p>
<p>1) Call <code><a href="VarSetCapacity.htm">VarSetCapacity</a>(MyStruct, 123, 0)</code> to ensure that the target variable is large enough to hold the structure's data. Replace 123 with a number that is at least as large as the size of the structure. Specifying zero as the last parameter is optional; it initializes all members to be binary zero, which is typically used to avoid calling NumPut as often in the next step.</p>
<p>2) If the target function uses the values initially in the structure, call <code><a href="NumPut.htm">NumPut</a>(123, MyStruct, 4, "UInt")</code> to initialize any members that should be non-zero. Replace <code>123</code> with the integer to be put into the target member (or specify <code>&amp;Var</code> to store the <a href="../Variables.htm#amp">address</a> of a variable). Replace <code>4</code> with the offset of the target member (see step #4 for description of &quot;offset&quot;). Replace <code>"UInt"</code> with the appropriate type or omit it if the member is a pointer or handle.</p>
<p>3) Call the target function, passing the <a href="../Variables.htm#amp">address</a> of <em>MyStruct</em> as a Ptr argument. For example, <code>DllCall(&quot;MyDll\MyFunc&quot;, &quot;Ptr&quot;, <strong>&amp;</strong>MyStruct)</code>. The function will examine and/or change some of the members.</p>
<p>4) Use <code>MyInteger := <a href="NumGet.htm">NumGet</a>(MyStruct, 4, "UInt")</code> to retrieve any desired integers from the structure. Replace <code>4</code> with the offset of the target member in the structure. The first member is always at offset 0. The second member is at offset 0 plus the size of the first member (typically 4). Members beyond the second are at the offset of the previous member plus the size of the previous member. Most members -- such as DWORD, Int, and <a href="#Int">other types of 32-bit integers</a> -- are 4 bytes in size. Replace <code>"UInt"</code> with the appropriate type or omit it if the member is a pointer or handle.</p>
<p>See <a href="#ExStruct">Structure Examples</a> for actual usages.</p>

<h2 id="limits">已知限制</h2>
<p>When a <a href="../Variables.htm#amp">variable's address</a> (e.g. <code>&amp;MyVar</code>) is passed to a function and that function alters the length of the variable's contents, subsequent uses of the variable may behave incorrectly. To fix this, do one of the following: 1) Pass <em>MyVar</em> as a <a href="#str">&quot;Str&quot;</a> argument rather than as a Ptr/address; 2) Call <code><a href="VarSetCapacity.htm#neg1">VarSetCapacity</a>(MyVar, -1)</code> to update the variable's internally-stored length after calling DllCall.</p>
<p>Any binary zero stored in a variable by a function hides all data to the right of the zero; that is, such data cannot be accessed or changed by most built-in functions. However, such data can be manipulated by the <a href="../Variables.htm#amp">address operator</a> and <a href="NumPut.htm">NumPut</a>/<a href="NumGet.htm">NumGet</a>, as well as DllCall itself.</p>
<p>A function that returns the address of one of the strings that was passed into it might return an identical string in a different memory address than expected. For example calling <code>CharLower(CharUpper(MyVar))</code> in a programming language would convert <em>MyVar</em>'s contents to lowercase. But when the same is done with DllCall, <em>MyVar</em> would be uppercase after the following call because CharLower would have operated on a different/temporary string whose contents were identical to <em>MyVar</em>:</p>
<pre>MyVar := "ABC"
result := DllCall(&quot;CharLower&quot;, &quot;<strong><u>Str</u></strong>&quot;, DllCall(&quot;CharUpper&quot;, &quot;Str&quot;, MyVar, &quot;<strong><u>Str</u></strong>&quot;), &quot;Str&quot;)</pre>
<p>To work around this, change the two underlined &quot;Str&quot; values above to Ptr. This interprets CharUpper's return value as a pure address that will get passed as an integer to CharLower.</p>
<p>Certain limitations may be encountered when dealing with strings. For details, 请参阅 <a href="../Compat.htm#DllCall">Script Compatibility</a>.</p>

<h2 id="COM">Component Object Model (COM)</h2>
<p>COM objects which are accessible to VBScript and similar languages are typically also accessible to AutoHotkey via <a href="ComObjCreate.htm">ComObjCreate</a>, <a href="ComObjGet.htm">ComObjGet</a> or <a href="ComObjActive.htm">ComObjActive</a> and the built-in <a href="../Objects.htm#Usage_Objects">object syntax</a>.</p>
COM objects which don't support <a href="http://msdn.microsoft.com/en-us/library/ms221608.aspx">IDispatch</a> can be used with DllCall by retrieving the address of a function from the virtual function table of the object's interface. For more details, see <a href="#ExTaskbar">the example</a> further below. However, it is usually better to use <a href="ComCall.htm">ComCall</a>, which streamlines this process.</p>

<h2 id="dotnet">.NET Framework</h2>
<p>.NET Framework libraries are executed by a "virtual machine" known as the Common Language Runtime, or CLR. That being the case, .NET DLL files are formatted differently to normal DLL files, and generally do not contain any functions which DllCall is capable of calling.</p>
<p>However, AutoHotkey can utilize the CLR through <a href="https://docs.microsoft.com/en-us/dotnet/standard/native-interop/com-callable-wrapper">COM callable wrappers</a>. Unless the library is also registered as a general COM component, the CLR itself must first be manually initialized via DllCall. For details, see <a href="https://www.autohotkey.com/forum/topic26191.html">.NET Framework Interop</a> (which currently utilizes DllCall and is not compatible with AutoHotkey v2).</p>

<h2 id="Related">相关</h2>
<p><a href="../Compat.htm#DllCall">Script Compatibility</a>, <a href="BufferAlloc.htm">BufferAlloc</a>, <a href="../objects/Buffer.htm">Buffer object</a>, <a href="ComCall.htm">ComCall</a>, <a href="PostMessage.htm">PostMessage</a>, <a href="OnMessage.htm">OnMessage</a>, <a href="CallbackCreate.htm">CallbackCreate</a>, <a href="Run.htm">Run</a>, <a href="VarSetCapacity.htm">VarSetCapacity</a>, <a href="../Functions.htm">Functions</a>, <a href="SysGet.htm">SysGet</a>, <a href="_DllLoad.htm">#DllLoad</a>, <a href="https://docs.microsoft.com/en-au/windows/win32/apiindex/windows-api-list">Windows API Index</a></p>

<h2 id="Examples">示例</h2>
<div class="ex" id="ExMessageBox">
<p><a href="#ExMessageBox">#1</a>: 调用 Windows API 函数 &quot;MessageBox&quot; 并报告用户按下了哪个按钮.</p>
<pre>WhichButton := DllCall(&quot;MessageBox&quot;, &quot;Int&quot;, 0, &quot;Str&quot;, &quot;Press Yes or No&quot;, &quot;Str&quot;, &quot;Title of box&quot;, &quot;Int&quot;, 4)
MsgBox "You pressed button #" WhichButton</pre>
</div>

<div class="ex" id="ExWallpaper">
<p><a href="#ExWallpaper">#2</a>: 将桌面墙纸更改为指定的位图(.bmp) 文件.</p>
<pre>DllCall(&quot;SystemParametersInfo&quot;, &quot;UInt&quot;, 0x14, &quot;UInt&quot;, 0, &quot;Str&quot;, A_WinDir <strong>.</strong> &quot;\winnt.bmp&quot;, &quot;UInt&quot;, 2)</pre>
</div>

<div class="ex" id="ExIsWindowVisible">
<p><a href="#ExIsWindowVisible">#3</a>: 调用 API 函数 &quot;IsWindowVisible&quot; 来判断记事本窗口是否可见.</p>
<pre>DetectHiddenWindows True
if not DllCall(&quot;IsWindowVisible&quot;, &quot;Ptr&quot;, WinExist(&quot;Untitled - Notepad&quot;))  <em>; WinExist 返回 HWND.</em>
    MsgBox "The window is not visible."</pre>
</div>

<div class="ex" id="ExWsprintf">
<p><a href="#ExWsprintf">#4</a>: 调用 API 的 wsprintf() 函数来给数字 432 加上前导零以填充到 10 个字符的长度(0000000432).</p>
<pre>ZeroPaddedNumber := BufferAlloc(20)  <em>; 确保缓冲足够大以便容纳新的字符串.</em>
DllCall(&quot;wsprintf&quot;, &quot;Ptr&quot;, ZeroPaddedNumber, &quot;Str&quot;, &quot;%010d&quot;, &quot;Int&quot;, 432, &quot;Cdecl&quot;)  <em>; 需要 Cdecl 调用约定.</em>
MsgBox StrGet(ZeroPaddedNumber)

<em>; 此外, 使用 <a href="Format.htm">Format</a> 函数配合 0 标志可以达到同样的效果:</em>
MsgBox Format("{:010}", 432)
</pre>
</div>

<div class="ex" id="ExQPC">
<p><a href="#ExQPC">#5</a>: 演示 QueryPerformanceCounter(), 它提供了比 <a href="../Variables.htm#TickCount">A_TickCount</a> 的 10ms 更高的精确度.</p>
<pre>DllCall(&quot;QueryPerformanceFrequency&quot;, &quot;Int64*&quot;, freq)
DllCall(&quot;QueryPerformanceCounter&quot;, &quot;Int64*&quot;, CounterBefore)
Sleep 1000
DllCall(&quot;QueryPerformanceCounter&quot;, &quot;Int64*&quot;, CounterAfter)
MsgBox &quot;Elapsed QPC time is &quot; . (CounterAfter - CounterBefore) / freq * 1000 &quot; ms&quot;</pre>
</div>

<div class="ex" id="ExMouseSpeed">
<p><a href="#ExMouseSpeed">#6</a>: 这是个临时减慢鼠标移动速度的热键, 这样有助于准确定位. 按住 <kbd>F1</kbd> 来降低鼠标速度. 释放后则恢复原来的速度.</p>
<pre>F1::
SPI_GETMOUSESPEED := 0x70
SPI_SETMOUSESPEED := 0x71
<em>; 获取鼠标当前的速度以便稍后恢复:</em>
DllCall(&quot;SystemParametersInfo&quot;, &quot;UInt&quot;, SPI_GETMOUSESPEED, &quot;UInt&quot;, 0, &quot;UIntP&quot;, OrigMouseSpeed, &quot;UInt&quot;, 0)
<em>; 现在在倒数第二个参数中设置较低的速度 (范围为 1-20, 10 是默认值):</em>
DllCall(&quot;SystemParametersInfo&quot;, &quot;UInt&quot;, SPI_SETMOUSESPEED, &quot;UInt&quot;, 0, &quot;Ptr&quot;, <strong>3</strong>, &quot;UInt&quot;, 0)
KeyWait "F1"  <em>; 这里避免了由于键盘的重复特性导致再次执行 DllCall.</em>
return

F1 up::DllCall(&quot;SystemParametersInfo&quot;, &quot;UInt&quot;, 0x71, &quot;UInt&quot;, 0, &quot;Ptr&quot;, OrigMouseSpeed, &quot;UInt&quot;, 0)  <em>; 恢复原来的速度.</em></pre>
</div>

<div class="ex" id="ExWatchScrollBar">
<p><a href="#ExWatchScrollBar">#7</a>: 监视活动窗口并显示其焦点控件中的垂直滚动条的位置(实时更新).</p>
<pre>SetTimer "WatchScrollBar", 100

WatchScrollBar()
{
    FocusedHwnd := ControlGetFocus("A")
    if !FocusedHwnd  <em>; 没有焦点控件.</em>
        return
    <em>; 在工具提示中显示垂直或水平滚动条的位置:</em>
    ToolTip(DllCall("GetScrollPos", "Ptr", FocusedHwnd, "Int", 1))  <em>;  最后一个参数 1 表示 SB_VERT, 而 0 表示 SB_HORZ.</em>
}</pre>
</div>

<div class="ex" id="ExFile">
<p><a href="#ExFile">#8</a>: 这是个可运行脚本, 它写入一些文本到文件, 然后从文件读取回内存. 在同时读取或写入多个文件时, 使用这种方法可以改善性能. 此外, 使用 <a href="FileOpen.htm">FileOpen</a> 可以实现<a href="FileOpen.htm#ExWriteRead">同样的目的</a>.</p>
<pre>FileName := FileSelect("S16",, "Create a new file:")
if FileName = ""
    return
GENERIC_WRITE := 0x40000000  <em>; 以写入而不是读取的方式打开文件.</em>
CREATE_ALWAYS := 2  <em>; 创建新文件(覆盖任何现有的文件).</em>
hFile := DllCall(&quot;CreateFile&quot;, &quot;Str&quot;, FileName, &quot;UInt&quot;, GENERIC_WRITE, &quot;UInt&quot;, 0, &quot;Ptr&quot;, 0, &quot;UInt&quot;, CREATE_ALWAYS, &quot;UInt&quot;, 0, &quot;Ptr&quot;, 0, &quot;Ptr&quot;)
if !hFile
{
    MsgBox "Can't open '" FileName "' for writing."
    return
}
TestString := "This is a test string.`r`n"  <em>; 通过这种方式写入内容到文件时, 要使用 `r`n 而不是 `n 来开始新行.</em>
StrLen := StrLen(TestString) * (A_IsUnicode ? 2 : 1)
DllCall(&quot;WriteFile&quot;, &quot;Ptr&quot;, hFile, &quot;Str&quot;, TestString, &quot;UInt&quot;, StrLen, &quot;UIntP&quot;, BytesActuallyWritten, &quot;Ptr&quot;, 0)
DllCall(&quot;CloseHandle&quot;, &quot;Ptr&quot;, hFile)  <em>; 关闭文件.</em>

<em>; 现在已经把内容写入文件了, 重新把它们读取回内存中.</em>
GENERIC_READ := 0x80000000  <em>; 以读取而不是写入的方式来打开文件.</em>
OPEN_EXISTING := 3  <em>; 此标志表示要打开的文件必须已经存在.</em>
FILE_SHARE_READ := 0x1 <em>; 这个和下一个标志表示其他进程是否可以打开我们已经打开的文件.</em>
FILE_SHARE_WRITE := 0x2
hFile := DllCall(&quot;CreateFile&quot;, &quot;Str&quot;, FileName, &quot;UInt&quot;, GENERIC_READ, &quot;UInt&quot;, FILE_SHARE_READ|FILE_SHARE_WRITE, &quot;Ptr&quot;, 0, &quot;UInt&quot;, OPEN_EXISTING, &quot;UInt&quot;, 0, &quot;Ptr&quot;, 0)
if !hFile
{
    MsgBox "Can't open '" FileName "' for reading."
    return
}
<em>; 为要读取的字符串分配一块内存:</em>
Buf := BufferAlloc(StrLen)
DllCall(&quot;ReadFile&quot;, &quot;Ptr&quot;, hFile, &quot;Ptr&quot;, Buf, &quot;UInt&quot;, Buf.Size, &quot;UIntP&quot;, BytesActuallyRead, &quot;Ptr&quot;, 0)
DllCall(&quot;CloseHandle&quot;, &quot;Ptr&quot;, hFile)  <em>; 关闭文件.</em>
MsgBox "The following string was read from the file: " StrGet(Buf)</pre>
</div>

<div class="ex" id="ExHideCursor">
<p><a href="#ExHideCursor">#9</a>: 当您按下 <kbd>Win</kbd>+<kbd>C</kbd> 时, 隐藏鼠标光标. 稍后若要显示光标, 请再次按下热键.</p>
<pre>OnExit (*) =&gt; SystemCursor("Show")  <em>; 确保到脚本退出时鼠标光标是显示的.</em>

#c::SystemCursor("Toggle")  <em>; Win+C 热键用来切换鼠标光标的显示和隐藏.</em>

SystemCursor(cmd)  <em>; cmd = "Show|Hide|Toggle|Reload"</em>
{
    static visible := true, c := Map()
    static sys_cursors := [32512, 32513, 32514, 32515, 32516, 32642
                         , 32643, 32644, 32645, 32646, 32648, 32649, 32650]
    if (cmd = "Reload" or !c.Count)  <em>; 在请求或首次调用时进行重载.</em>
    {
        for i, id in sys_cursors
        {
            h_cursor  := DllCall("LoadCursor", "Ptr", 0, "Ptr", id)
            h_default := DllCall("CopyImage", "Ptr", h_cursor, "UInt", 2
                , "Int", 0, "Int", 0, "UInt", 0)
            h_blank   := DllCall("CreateCursor", "Ptr", 0, "Int", 0, "Int", 0
                , "Int", 32, "Int", 32
                , "Ptr", BufferAlloc(32*4, 0xFF)
                , "Ptr", BufferAlloc(32*4, 0))
            c[id] := {"default": h_default, "blank": h_blank}
        }
    }
    switch cmd
    {
      case "Show": visible := true
      case "Hide": visible := false
      case "Toggle": visible := !visible
      default: return
    }
    for id, handles in c
    {
        h_cursor := DllCall("CopyImage"
            , "Ptr", visible ? handles.default : handles.blank
            , "UInt", 2, "Int", 0, "Int", 0, "UInt", 0)
        DllCall("SetSystemCursor", "Ptr", h_cursor, "UInt", id)
    }
}</pre>
</div>

<div class="ex" id="ExStruct">
<p><a href="#ExStruct">#10</a>: 结构的例子. 把 RECT 结构的地址传递给 GetWindowRect(), 它会把 窗口的左, 上, 右和下边的位置(相对于屏幕) 存入结构的成员中.</p>
<pre>Run "Notepad"
WinWait "Untitled - Notepad"  <em>; 这里同时设置了 "<a href="../misc/WinTitle.htm#LastFoundWindow">上次找到的窗口</a>" 以用于下面的 WinExist.</em>
Rect := BufferAlloc(16)  <em>; RECT 结构由四个 32 位整数组成(即 4*4=16).</em>
DllCall(&quot;GetWindowRect&quot;, &quot;Ptr&quot;, WinExist(), &quot;Ptr&quot;, Rect)  <em>; WinExist 返回 HWND.</em>
L := NumGet(Rect, 0, "Int"), T := NumGet(Rect, 4, "Int")
R := NumGet(Rect, 8, "Int"), B := NumGet(Rect, 12, "Int")
MsgBox Format("Left {1} Top {2} Right {3} Bottom {4}", L, T, R, B)</pre>

<div class="ex" id="ExStructRect">
<p><a href="#ExStructRect">#11</a>: 结构的例子. 把 RECT 结构的地址传递给 FillRect(), 这个结构表示需要临时描绘为红色的屏幕区域.</p>
<pre>Rect := BufferAlloc(16)  <em>; 设置容量为 4 个 4 字节整型.</em>
<a href="NumPut.htm">NumPut</a>( "Int", 0                  <em>; 左</em>
      , "Int", 0                  <em>; 上</em>
      , "Int", A_ScreenWidth//2   <em>; 右</em>
      , "Int", A_ScreenHeight//2  <em>; 下</em>
      , Rect)
hDC := DllCall(&quot;GetDC&quot;, "Ptr", 0, "Ptr")  <em>; 传递零来获取桌面的设备上下文.</em>
hBrush := DllCall(&quot;CreateSolidBrush&quot;, "UInt", 0x0000FF, "Ptr")  <em>; 创建红色画刷(0x0000FF 是 BGR 格式).</em>
DllCall(&quot;FillRect&quot;, "Ptr", hDC, "Ptr", Rect, "Ptr", hBrush)  <em>; 使用上面的画刷填充指定的矩形.</em>
DllCall(&quot;ReleaseDC&quot;, "Ptr", 0, "Ptr", hDC)  <em>; 清理.</em>
DllCall(&quot;DeleteObject&quot;, "Ptr", hBrush)  <em>; 清理.</em></pre>
</div>

<div class="ex" id="ExSystemTime">
<p><a href="#ExSystemTime">#12</a>: 结构的例子. 将系统的时钟更改为指定的日期和时间. 请注意改变时间为将来的日期可能会导致计划任务提早运行!</p>
<pre>SetSystemTime(&quot;20051008142211&quot;)  <em>; 传递<a href="FileSetTime.htm#YYYYMMDD">时间戳</a>(本地的, 非 UTC).</em>

SetSystemTime(YYYYMMDDHHMISS)
<em>; 设置系统时钟为指定的日期和时间.
; 调用者必须确保传入的参数是有效的日期时间戳
; (本地时间, 非 UTC). 成功时返回非零值, 否则返回零.</em>
{
    <em>; 把参数从本地时间转换为 UTC 以便用于 SetSystemTime().</em>
    UTC_Delta := DateDiff(A_Now, A_NowUTC, "Seconds")  <em>; 取整后秒数会更精确.</em>
    UTC_Delta := Round(-UTC_Delta/60)  <em>; 取整到最近的分钟数以确保精度.</em>
    YYYYMMDDHHMISS := DateAdd(YYYYMMDDHHMISS, UTC_Delta, "Minutes")  <em>; 对本地时间应用偏移来转换到 UTC.</em>

    SystemTime := BufferAlloc(16)  <em>; 此结构由 8 个 UShort 组成(即 8*2=16).</em>

    <a href="NumPut.htm">NumPut</a>( "UShort", SubStr(YYYYMMDDHHMISS, 1, 4)  <em>; YYYY(年)</em>
          , "UShort", SubStr(YYYYMMDDHHMISS, 5, 2)  <em>; MM(月, 01-12)</em>
          , "UShort", 0                             <em>; Unused (day of week)</em>
          , "UShort", SubStr(YYYYMMDDHHMISS, 7, 2)  <em>; DD(日)</em>
          , "UShort", SubStr(YYYYMMDDHHMISS, 9, 2)  <em>; HH(小时, 00-24)</em>
          , "UShort", SubStr(YYYYMMDDHHMISS, 11, 2) <em>; MI(分钟)</em>
          , "UShort", SubStr(YYYYMMDDHHMISS, 13, 2) <em>; SS(秒)</em>
          , "UShort", 0                             <em>; Unused (millisecond)</em>
          , SystemTime)

    return DllCall(&quot;SetSystemTime&quot;, &quot;Ptr&quot;, SystemTime)
}</pre>
</div>

<p>更多结构的例子:</p>
<ul>
  <li>请参阅 <a href="../scripts/WinLIRC.htm">WinLIRC 客户端脚本</a>演示来学习如何使用 DllCall 来创建
到 TCP/IP 服务器的连接并从那里接收数据.</li>
  <li>操作系统提供了标准的对话框让用户选取字体和/或颜色或图标.
  这些对话框使用了结构, 并可以在这里找到演示的例子: <a href="https://github.com/majkinetor/mm-autohotkey/tree/master/Dlg">GitHub</a>.</li>
</ul>
</div>

<div class="ex" id="ExTaskbar">
<p><a href="#ExTaskbar">#13</a>: 从任务栏移除活动窗口 3 秒. Compare this to the <a href="ComCall.htm#ExTaskbar">equivalent ComCall example</a>.</p>
<pre><em>/*
  Methods in <a href="http://msdn.microsoft.com/en-us/library/bb774652.aspx">ITaskbarList</a>'s VTable:
    IUnknown:
      0 QueryInterface  -- use <a href="ComObjQuery.htm">ComObjQuery</a> instead
      1 AddRef          -- use <a href="ObjAddRef.htm">ObjAddRef</a> instead
      2 Release         -- use <a href="ObjAddRef.htm">ObjRelease</a> instead
    ITaskbarList:
      3 HrInit
      4 AddTab
      5 DeleteTab
      6 ActivateTab
      7 SetActiveAlt
*/</em>
IID_ITaskbarList  := "{56FDF342-FD6D-11d0-958A-006097C9A090}"
CLSID_TaskbarList := "{56FDF344-FD6D-11d0-958A-006097C9A090}"

<em>; 创建 TaskbarList 对象并把它的地址保存到 tbl.</em>
tbl := <a href="ComObjCreate.htm">ComObjCreate</a>(CLSID_TaskbarList, IID_ITaskbarList)

activeHwnd := WinExist("A")

DllCall(vtable(tbl,3), "ptr", tbl)                     <em>; tbl.<a href="http://msdn.microsoft.com/en-us/library/bb774650.aspx">HrInit</a>()</em>
DllCall(vtable(tbl,5), "ptr", tbl, "ptr", activeHwnd)  <em>; tbl.<a href="http://msdn.microsoft.com/en-us/library/bb774648.aspx">DeleteTab</a>(activeHwnd)</em>
Sleep 3000
DllCall(vtable(tbl,4), "ptr", tbl, "ptr", activeHwnd)  <em>; tbl.<a href="http://msdn.microsoft.com/en-us/library/bb774646.aspx">AddTab</a>(activeHwnd)</em>

<em>; 非包装接口指针必须手动释放.</em>
ObjRelease(tbl)

vtable(ptr, n) {
    <em>; NumGet(ptr+0) 返回对象的虚函数表(简称为 vtable) 的地址
    ; 表达式的其余部分从
    ; vtable 中获取第 n 个函数的地址.</em>
    return NumGet(NumGet(ptr+0), n*A_PtrSize)
}
</pre>
</div>

</body>
</html>